#!/usr/perl5/bin/perl
#
# Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
#

#
# This script is a stress test for ::Exacct and libexacct.
# See README for details.
#

use strict;
use warnings;
use blib;
use POSIX qw(:sys_wait_h);

use Sun::Solaris::Exacct qw(:ALL);
use Sun::Solaris::Exacct::Catalog qw(:ALL);
use Sun::Solaris::Exacct::Object qw(:ALL);
use Sun::Solaris::Exacct::File qw(:ALL);
use Fcntl;

our $exit = 0;
our $ono  = 1;
our $maxono = 1000;	# max = 16777216 (2^24)

#
# Dump an object.
#
sub dump_object
{
	my ($obj, $fh, $indent) = @_;
	$fh ||= \*STDOUT;
	$indent ||= 0;
	my @cat = $obj->catalog()->value();
	my $istr = '  ' x $indent;

	if ($obj->type() == &EO_ITEM) {
		printf $fh ("%sITEM\n%s  Catalog = %s|%s|%d\n", 
		   $istr, $istr, @cat);
		$indent++;
		my $val = $obj->value();
		if (ref($val)) {
			dump_object($val, $fh, $indent);
		} else {
			printf $fh ("%s  Value = %s\n", $istr, $val);
		}
	} else {
		printf $fh ("%sGROUP\n%s  Catalog = %s|%s|%d\n",
		    $istr, $istr, @cat);
		$indent++;
		foreach my $val ($obj->value()) {
			dump_object($val, $fh, $indent);
		}
		printf $fh ("%sENDGROUP\n", $istr);
	}
}

#
# Dump a list of objects.
#
sub dump_objects
{
	my ($fh, $objs) = @_;
	foreach my $o (@$objs) {
		dump_object($o, $fh);
	}
}

#
# Build up a set of random objects.
#
sub gen_objs
{
	my ($nobjs, $embed) = @_;
	$nobjs += $ono;
	$embed ||= 0;
	my @objs;
	while ($ono < $nobjs) {
		my $rt = int(rand(9)) + 1;
		$rt = 15 if ($rt >= 9);	# Group.
		$rt <<= 28;
		if ($rt == &EXT_UINT8) {
			push(@objs, ea_new_item($rt | $ono++, 8));
		} elsif ($rt == &EXT_UINT16) {
			push(@objs, ea_new_item($rt | $ono++, 16));
		} elsif ($rt == &EXT_UINT32) {
			push(@objs, ea_new_item($rt | $ono++, 32));
		} elsif ($rt == &EXT_UINT64) {
			push(@objs, ea_new_item($rt | $ono++, 64));
		} elsif ($rt == &EXT_DOUBLE) {
			push(@objs, ea_new_item($rt | $ono++,
			    123456789.123456789));
		} elsif ($rt == &EXT_STRING) {
			push(@objs, ea_new_item($rt | $ono++, "string"));
		} elsif ($rt == &EXT_EXACCT_OBJECT) {
			my $o = $ono++;
			my $i = int(rand($nobjs - $ono)) + 1;
			push(@objs, ea_new_item($rt | $o, gen_objs($i, 1)));
		} elsif ($rt == &EXT_RAW) {
			push(@objs, ea_new_item($rt | $ono++, "RAWrawRAW"));
		} elsif ($rt == &EXT_GROUP) {
			my $o = $ono++;
			my $i = int(rand($nobjs - $ono + 1));
			push(@objs, ea_new_group($rt | $o, gen_objs($i)));
		}

		# If for an embedded object, just return 1 object.
		last if ($embed);
	}
	return(@objs);
}

#
# Main routine.
#
$| = 1;
$SIG{INT} = $SIG{TERM} = $SIG{HUP} = sub { $exit = 1; };
my $iters = 0;
while (! $exit) {
	print(".");

	# Generate and output some random records.
	my $f = ea_new_file("/tmp/wr.$$", &O_RDWR | &O_CREAT | &O_TRUNC)
	    || die("\ncreate /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	my @outobjs = gen_objs($maxono);
	$f->write(@outobjs);
	$f = undef;
	open($f, ">/tmp/wr1.$$") || die("\nopen /tmp/wr1.$$ failed: $!\n");
	dump_objects($f, \@outobjs);
	close($f);
	@outobjs = ();

	# Scan the file forwards with next.
	$f = ea_new_file("/tmp/wr.$$", &O_RDONLY)
	    || die("\nopen /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	while ($f->next()) {
		;
	}
	die("\nnext /tmp/wr.$$ failed: ", ea_error_str(), "\n")
	    unless (ea_error() == EXR_EOF);
	$f = undef;

	# Scan the file backwards with previous.
	$f = ea_new_file("/tmp/wr.$$", &O_RDONLY, aflags => &EO_TAIL)
	    || die("\nopen /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	while ($f->previous()) {
		;
	}
	die("\nprevious /tmp/wr.$$ failed: ", ea_error_str(), "\n")
	    unless (ea_error() == EXR_EOF);
	$f = undef;

	# Read the file forwards with get.
	my @inobjs = ();
	$f = ea_new_file("/tmp/wr.$$", &O_RDONLY)
	    || die("\nopen /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	while (my $obj = $f->get()) {
		push(@inobjs, $obj);
	}
	die("\nget /tmp/wr.$$ failed: ", ea_error_str(), "\n")
	    unless (ea_error() == EXR_EOF);
	$f = undef;

	# Dump the objects and compare with original.
	open($f, ">/tmp/wr2.$$") || die("\nopen /tmp/wr2.$$ failed: $!\n");
	dump_objects($f, \@inobjs);
	close($f);
	if (system("cmp -s /tmp/wr1.$$ /tmp/wr2.$$") != 0) {
		die("\nget cmp failed /tmp/wr2.$$\n");
	}

	# Read the file forwards with next and get.
	@inobjs = ();
	$f = ea_new_file("/tmp/wr.$$", &O_RDONLY)
	    || die("\nopen /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	while ($f->next()) {
		my $obj = $f->get();
		push(@inobjs, $obj);
	}
	die("\nnext/get /tmp/wr.$$ failed: ", ea_error_str(), "\n")
	    unless (ea_error() == EXR_EOF);
	$f = undef;

	# Dump the objects and compare with original.
	open($f, ">/tmp/wr2.$$") || die("\nopen /tmp/wr2.$$ failed: $!\n");
	dump_objects($f, \@inobjs);
	close($f);
	if (system("cmp -s /tmp/wr1.$$ /tmp/wr2.$$") != 0) {
		die("\nnext/get cmp failed /tmp/wr2.$$\n");
	}

	# Read the file backwards with previous and get.
	@inobjs = ();
	$f = ea_new_file("/tmp/wr.$$", &O_RDONLY, aflags => &EO_TAIL)
	    || die("\nopen /tmp/wr.$$ failed: ", ea_error_str(), "\n");
	while ($f->previous()) {
		my $obj = $f->get();
		$f->previous();
		unshift(@inobjs, $obj);
	}
	die("\nprevious/get /tmp/wr.$$ failed: ", ea_error_str(), "\n")
	    unless (ea_error() == EXR_EOF);
	$f = undef;

	# Dump the objects and compare with original.
	open($f, ">/tmp/wr2.$$") || die("\nopen /tmp/wr2.$$ failed: $!\n");
	dump_objects($f, \@inobjs);
	close($f);
	if (system("cmp -s /tmp/wr1.$$ /tmp/wr2.$$") != 0) {
		die("\nprevious/get cmp failed /tmp/wr2.$$\n");
	}

	# Run randtest on the file.
	foreach my $sz (qw(5 10 50 100)) {
		my $s = system ("./randtest 1000 $sz /tmp/wr.$$") >> 8;
		if ($s == 2) {
			$exit = 1;
		} elsif ($s != 0) {
			die("randtest 1000 $sz /tmp/wr.$$ failed $s\n");
		}
	}

	$iters++;
}
unlink("/tmp/wr.$$", "/tmp/wr1.$$", "/tmp/wr2.$$") ||
    die("\nCan't cleanup: $!\n");
print("\n$iters iterations completed\n");
